//
//  ZLSystemCapture.m
//  Video
//
//  Created by os on 2021/3/26.
//

#import "ZLSystemCapture.h"

@interface ZLSystemCapture () <AVCaptureAudioDataOutputSampleBufferDelegate, AVCaptureVideoDataOutputSampleBufferDelegate>

#pragma mark 控制相关
/// 是否进行
@property (nonatomic, assign) BOOL isRunning;

#pragma mark 公共
/// 会话
@property (nonatomic, strong) AVCaptureSession *captureSession;
/// 代理队列
@property (nonatomic, strong) dispatch_queue_t captureQueue;

#pragma mark 音频相关
/// 音频设备
@property (nonatomic, strong) AVCaptureDeviceInput *audioInputDevice;
/// 输出数据接收
@property (nonatomic, strong) AVCaptureAudioDataOutput *audioDataOutput;
@property (nonatomic, strong) AVCaptureConnection *audioConnection;

#pragma mark 视频相关
/// 当前使用的视频设备
@property (nonatomic, weak) AVCaptureDeviceInput *videoInputDevice;
/// 前后摄像头
@property (nonatomic, strong) AVCaptureDeviceInput *frontCamera;
@property (nonatomic, strong) AVCaptureDeviceInput *backCamera;
/// 输出数据接收
@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput;
@property (nonatomic, strong) AVCaptureConnection *videoConnection;
/// 预览层
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *preLayer;
@property (nonatomic, assign) CGSize prelayerSize;

@end

@implementation ZLSystemCapture {
    /// 捕捉类型
    ZLSystemCaptureType captureType;
}

/// 初始化
- (instancetype)initWithType:(ZLSystemCaptureType)type {
    if (self = [super init]) {
        captureType = type;
    }
    return self;
}

/// 捕获音频时调用
- (void)prepare {
    [self prepareWithPreviewsize:CGSizeZero];
}

/// 捕获视频时调用（预览层大小，添加到 view 上用来显示）
- (void)prepareWithPreviewsize:(CGSize)size {
    _prelayerSize = size;
    if (captureType == ZLSystemCaptureTypeAudio) {
        [self setupAudio];
    } else if (captureType == ZLSystemCaptureTypeVideo) {
        [self setupVideo];
    } else if (captureType == ZLSystemCaptureTypeAll) {
        [self setupAudio];
        [self setupVideo];
    }
}

#pragma mark - Control start/stop capture or change camera

- (void)start {
    if (!self.isRunning) {
        self.isRunning = YES;
        [self.captureSession startRunning];
    }
}

- (void)stop {
    if (self.isRunning) {
        self.isRunning = NO;
        [self.captureSession stopRunning];
    }
}

- (void)changeCamera {
    [self.captureSession beginConfiguration];
    [self.captureSession removeInput:self.videoInputDevice];
    if ([self.videoInputDevice isEqual:self.frontCamera]) {
        self.videoInputDevice = self.backCamera;
    } else if ([self.videoInputDevice isEqual:self.backCamera]) {
        self.videoInputDevice = self.frontCamera;
    }
    [self.captureSession addInput:self.videoInputDevice];
    [self.captureSession commitConfiguration];
}

#pragma mark - init Audio/Video

- (void)setupAudio {
    // 麦克风设备
    AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
    // 将 audioDevice -> AVCaptureDeviceInput 对象
    self.audioInputDevice = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];
    // 音频输出
    self.audioDataOutput = [[AVCaptureAudioDataOutput alloc] init];
    [self.audioDataOutput setSampleBufferDelegate:self queue:self.captureQueue];
    // 配置
    [self.captureSession beginConfiguration];
    if ([self.captureSession canAddInput:self.audioInputDevice]) {
        [self.captureSession addInput:self.audioInputDevice];
    }
    if ([self.captureSession canAddOutput:self.audioDataOutput]) {
        [self.captureSession addOutput:self.audioDataOutput];
    }
    [self.captureSession commitConfiguration];
    
    self.audioConnection = [self.audioDataOutput connectionWithMediaType:AVMediaTypeAudio];
}

- (void)setupVideo {
    // 所有video设备
    NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    // 前置摄像头
    self.frontCamera = [AVCaptureDeviceInput deviceInputWithDevice:videoDevices.lastObject error:nil];
    // 后置摄像头
    self.backCamera = [AVCaptureDeviceInput deviceInputWithDevice:videoDevices.firstObject error:nil];
    // 设置当前设备为前置
    self.videoInputDevice = self.backCamera;
    // 视频输出
    self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
    [self.videoDataOutput setSampleBufferDelegate:self queue:self.captureQueue];
    [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];
    
    // kCVPixelBufferPixelFormatTypeKey 它指定像素的输出格式，这个参数直接影响到生成图像的成功与否
    // kCVPixelFormatType_420YpCbCr8BiPlanarFullRange  YUV420格式.
    [self.videoDataOutput setVideoSettings:@{(__bridge NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)}];
    
    // 配置
    [self.captureSession beginConfiguration];
    if ([self.captureSession canAddInput:self.videoInputDevice]) {
        [self.captureSession addInput:self.videoInputDevice];
    }
    if ([self.captureSession canAddOutput:self.videoDataOutput]) {
        [self.captureSession addOutput:self.videoDataOutput];
    }
    
    // 分辨率
    [self setVideoPreset];
    [self.captureSession commitConfiguration];
    
    // commit 后下面的代码才会有效
    self.videoConnection = [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
    // 设置视频输出方向
    self.videoConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
    
    // 设置 fps
    [self updateFps:25];
    
    // 设置预览层
    [self setupPreviewLayer];
}

/// 设置预览层
- (void)setupPreviewLayer {
    self.preLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    self.preLayer.frame = CGRectMake(0, 0, self.prelayerSize.width, self.prelayerSize.height);
    
    // 设置满屏
    self.preLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    [self.preview.layer addSublayer:self.preLayer];
}

/// 设置分辨率
- (void)setVideoPreset {
    if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset3840x2160]) {
        self.captureSession.sessionPreset = AVCaptureSessionPreset3840x2160;
        _width = 2160;
        _height = 3840;
    } else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {
        self.captureSession.sessionPreset = AVCaptureSessionPreset1920x1080;
        _width = 1080;
        _height = 1920;
    } else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
        self.captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
        _width = 720;
        _height = 1280;
    } else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) {
        self.captureSession.sessionPreset = AVCaptureSessionPreset640x480;
        _width = 480;
        _height = 640;
    } else {
        self.captureSession.sessionPreset = AVCaptureSessionPreset352x288;
        _width = 288;
        _height = 352;
    }
}

/// 设置 fps
- (void)updateFps:(NSInteger)fps {
    // 获取当前摄像头
    NSMutableArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    
    // 遍历所有设备（前后摄像头）
    for (AVCaptureDevice *device in videoDevices) {
        // 获取当前支持的最大 fps
        float maxRate = [(AVFrameRateRange *)[device.activeFormat.videoSupportedFrameRateRanges objectAtIndex:0] maxFrameRate];
        // 如果想要设置的 fps 小于或者等于最大 fps，就进行修改
        if (maxRate >= fps) {
            if ([device lockForConfiguration:nil]) {
                device.activeVideoMinFrameDuration = CMTimeMake(10, (int)(fps * 10));
                device.activeVideoMaxFrameDuration = device.activeVideoMinFrameDuration;
                [device unlockForConfiguration];
            }
        }
    }
}

#pragma mark - AVCaptureAudioDataOutputSampleBufferDelegate & AVCaptureVideoDataOutputSampleBufferDelegate

- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    
    if (connection == self.audioConnection) {
        if (self.delegate && [self.delegate respondsToSelector:@selector(captureSampleBuffer:type:)]) {
            [self.delegate captureSampleBuffer:sampleBuffer type:ZLSystemCaptureTypeAudio];
        }
    } else if (connection == self.videoConnection) {
        if (self.delegate && [self.delegate respondsToSelector:@selector(captureSampleBuffer:type:)]) {
            [self.delegate captureSampleBuffer:sampleBuffer type:ZLSystemCaptureTypeVideo];
        }
    }
    
}

#pragma mark - 授权相关

/// 麦克风授权  0 : 未授权 1: 已授权 -1: 拒绝
+ (int)checkMicrophoneAuthor {
    int result = 0;
    // 麦克风
    AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
    switch (permissionStatus) {
        case AVAudioSessionRecordPermissionUndetermined: {
            // 请求授权
            [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
                
            }];
            result = 0;
        }
            break;
        case AVAudioSessionRecordPermissionDenied: {
            // 请求被拒绝
            result = -1;
        }
            break;
        case AVAudioSessionRecordPermissionGranted: {
            // 请求已授权
            result = 1;
        }
            break;
    }
    return result;
}

/// 摄像头授权  0 : 未授权 1: 已授权 -1: 拒绝
+ (int)checkCameraAuthor {
    int result = 0;
    AVAuthorizationStatus videoStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    switch (videoStatus) {
        case AVAuthorizationStatusNotDetermined: {
            // 请求授权
            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                
            }];
            result = 0;
        }
            break;
        case AVAuthorizationStatusAuthorized: {
            // 请求已授权
            result = 1;
        }
            break;
        default: {
            // 请求被拒绝
            result = -1;
        }
            break;
    }
    return result;
}

#pragma mark - 销毁会话

- (void)destroyCaptureSession {
    if (self.captureSession) {
        if (captureType == ZLSystemCaptureTypeAudio) {
            [self.captureSession removeInput:self.audioInputDevice];
            [self.captureSession removeOutput:self.audioDataOutput];
        } else if (captureType == ZLSystemCaptureTypeVideo) {
            [self.captureSession removeInput:self.videoInputDevice];
            [self.captureSession removeOutput:self.videoDataOutput];
        } else if (captureType == ZLSystemCaptureTypeAll) {
            [self.captureSession removeInput:self.audioInputDevice];
            [self.captureSession removeOutput:self.audioDataOutput];
            [self.captureSession removeInput:self.videoInputDevice];
            [self.captureSession removeOutput:self.videoDataOutput];
        }
    }
    self.captureSession = nil;
}

#pragma mark - getter & setter

- (AVCaptureSession *)captureSession {
    if (!_captureSession) {
        _captureSession = [[AVCaptureSession alloc] init];
    }
    return _captureSession;
}

- (dispatch_queue_t)captureQueue {
    if (!_captureQueue) {
        _captureQueue = dispatch_queue_create("ZLCapture Queue", NULL);
    }
    return _captureQueue;
}

- (UIView *)preview {
    if (!_preview) {
        _preview = [[UIView alloc] init];
    }
    return _preview;
}

- (void)dealloc {
    NSLog(@"capture销毁...");
    [self destroyCaptureSession];
}

@end
